#include <QtCore/QStringList> // for QStringList
#include <QtCore/QTextStream> // for QTextStream
#include <QtCore/QTime> // for QTime
-#include <QtCore/QVector> // for QVector
#include <QtCore/QtGlobal> // for qAsConst, QAddConst<>::Type, qPrintable
#include "defs.h"
#include "strptime.h" // for strptime
#include "xcsv.h"
-#define MYNAME "XCSV"
-
-/* macros */
-constexpr char lat_dir(double a) {return a < 0.0 ? 'S' : 'N';}
-constexpr char lon_dir(double a) {return a < 0.0 ? 'W' : 'E';}
-/* convert excel time (days since 1900) to time_t and back again */
-constexpr double excel_to_timet(double a) {return (a - 25569.0) * 86400.0;}
-constexpr double timet_to_excel(double a) {return (a / 86400.0) + 25569.0;}
+#if CSVFMTS_ENABLED
-constexpr int gps_datum_wgs84 = 118; // GPS_Lookup_Datum_Index("WGS 84")
+#define MYNAME "XCSV"
/*
* Internal numeric value to associate with each keyword in a style file.
#include "xcsv_tokens.gperf" // for Perfect_Hash, xt_mapping
-#if CSVFMTS_ENABLED
-/****************************************************************************/
-/* obligatory global struct */
-/****************************************************************************/
-
-static XcsvFile* xcsv_file;
-static const XcsvStyle* xcsv_style;
-static double pathdist = 0;
-static double oldlon = 999;
-static double oldlat = 999;
-
-static int waypt_out_count = 0;
-static const route_head* csv_track = nullptr;
-static const route_head* csv_route = nullptr;
-
-struct xcsv_parse_data {
- QString rte_name;
- QString trk_name;
- bool new_track{false};
- double utm_northing{0};
- double utm_easting{0};
- double utm_zone{0};
- char utm_zonec{'N'};
- UrlLink* link_{nullptr};
- gpsbabel_optional::optional<bool> lat_dir_positive;
- gpsbabel_optional::optional<bool> lon_dir_positive;
-};
-
-static char* styleopt = nullptr;
-static char* snlenopt = nullptr;
-static char* snwhiteopt = nullptr;
-static char* snupperopt = nullptr;
-static char* snuniqueopt = nullptr;
-static char* prefer_shortnames = nullptr;
-static char* xcsv_urlbase = nullptr;
-static char* opt_datum = nullptr;;
-
-static const char* intstylebuf = nullptr;
-
-static
-QVector<arglist_t> xcsv_args = {
- {
- "style", &styleopt, "Full path to XCSV style file", nullptr,
- ARGTYPE_FILE | ARGTYPE_REQUIRED, ARG_NOMINMAX, nullptr
- },
- {
- "snlen", &snlenopt, "Max synthesized shortname length", nullptr,
- ARGTYPE_INT, "1", nullptr, nullptr
- },
- {
- "snwhite", &snwhiteopt, "Allow whitespace synth. shortnames",
- nullptr, ARGTYPE_BOOL, ARG_NOMINMAX, nullptr
- },
- {
- "snupper", &snupperopt, "UPPERCASE synth. shortnames",
- nullptr, ARGTYPE_BOOL, ARG_NOMINMAX, nullptr
- },
- {
- "snunique", &snuniqueopt, "Make synth. shortnames unique",
- nullptr, ARGTYPE_BOOL, ARG_NOMINMAX, nullptr
- },
- {
- "urlbase", &xcsv_urlbase, "Basename prepended to URL on output",
- nullptr, ARGTYPE_STRING, ARG_NOMINMAX, nullptr
- },
- {
- "prefer_shortnames", &prefer_shortnames,
- "Use shortname instead of description",
- nullptr, ARGTYPE_BOOL, ARG_NOMINMAX, nullptr
- },
- {
- "datum", &opt_datum, "GPS datum (def. WGS 84)",
- nullptr, ARGTYPE_STRING, ARG_NOMINMAX, nullptr
- },
-};
-
-/* something to map config file constants to chars */
-struct char_map_t {
- const QString key;
- const QString chars;
-};
-
/* a table of config file constants mapped to chars */
-static
-char_map_t xcsv_char_table[] = {
+const XcsvStyle::char_map_t XcsvStyle::xcsv_char_table[] = {
{ "COMMA", "," },
{ "COMMASPACE", ", " },
{ "SINGLEQUOTE", "'" },
};
// Given a keyword of "COMMASPACE", return ", ".
-static QString
-xcsv_get_char_from_constant_table(const QString& key)
+QString
+XcsvStyle::xcsv_get_char_from_constant_table(const QString& key)
{
static QHash<QString, QString> substitutions;
if (substitutions.empty()) {
- for (char_map_t* cm = xcsv_char_table; !cm->key.isNull(); cm++) {
+ for (const char_map_t* cm = xcsv_char_table; !cm->key.isNull(); cm++) {
substitutions.insert(cm->key, cm->chars);
}
}
// Remove outer quotes.
// Should probably be in csv_util.
-static QString dequote(const QString& in) {
+QString XcsvStyle::dequote(const QString& in) {
QString r = in.simplified();
if (r.startsWith("\"")) r = r.mid(1);
if (r.endsWith("\"")) r.chop(1);
return r;
}
-static void validate_fieldmap(const field_map& fmp, bool is_output) {
+void XcsvStyle::validate_fieldmap(const field_map& fmp, bool is_output) {
if (fmp.key.isEmpty()) {
Fatal() << MYNAME << ": xcsv style is missing" <<
(is_output ? "output" : "input") << "field type.";
/* xcsv_ifield_add() - add input field to ifield queue. */
/* usage: xcsv_ifield_add("DESCRIPTION", "", "%s") */
/*****************************************************************************/
-static void
-xcsv_ifield_add(XcsvStyle* style, const QString& qkey, const QString& qval, const QString& qpfc)
+void
+XcsvStyle::xcsv_ifield_add(XcsvStyle* style, const QString& qkey, const QString& qval, const QString& qpfc)
{
QByteArray key = qkey.toUtf8();
QByteArray val = qval.toUtf8();
/* xcsv_ofield_add() - add output field to ofield queue. */
/* usage: xcsv_ofield_add("LAT_DECIMAL", "", "%08.5lf") */
/*****************************************************************************/
-static void
-xcsv_ofield_add(XcsvStyle* style, const QString& qkey, const QString& qval, const QString& qpfc, unsigned options)
+void
+XcsvStyle::xcsv_ofield_add(XcsvStyle* style, const QString& qkey, const QString& qval, const QString& qpfc, unsigned options)
{
QByteArray key = qkey.toUtf8();
QByteArray val = qval.toUtf8();
style->ofields.append(fmp);
}
-static QDateTime
-yyyymmdd_to_time(const char* s)
+QDateTime
+XcsvFormat::yyyymmdd_to_time(const char* s)
{
QDate d = QDate::fromString(s, "yyyyMMdd");
return QDateTime(d);
/*
* sscanftime - Parse a date buffer using strftime format
*/
-static time_t
-sscanftime(const char* s, const char* format, const int gmt)
+time_t
+XcsvFormat::sscanftime(const char* s, const char* format, const int gmt)
{
struct tm stm;
memset(&stm, 0, sizeof(stm));
return 0;
}
-static time_t
-addhms(const char* s, const char* format)
+time_t
+XcsvFormat::addhms(const char* s, const char* format)
{
time_t tt = 0;
int hour = 0;
return tt;
}
-static QString
-writetime(const char* format, time_t t, bool gmt)
+QString
+XcsvFormat::writetime(const char* format, time_t t, bool gmt)
{
static struct tm* stmp;
return QString(tbuff);
}
-static QString
-writetime(const char* format, const gpsbabel::DateTime& t, bool gmt)
+QString
+XcsvFormat::writetime(const char* format, const gpsbabel::DateTime& t, bool gmt)
{
return writetime(format, t.toTime_t(), gmt);
}
-static QString
-writehms(const char* format, time_t t, int gmt)
+QString
+XcsvFormat::writehms(const char* format, time_t t, int gmt)
{
static struct tm no_time = tm();
static struct tm* stmp = &no_time;
(stmp->tm_hour >= 12 ? "PM" : "AM"));
}
-static QString
-writehms(const char* format, const gpsbabel::DateTime& t, int gmt)
+QString
+XcsvFormat::writehms(const char* format, const gpsbabel::DateTime& t, int gmt)
{
return writehms(format, t.toTime_t(), gmt);
}
-static long
-time_to_yyyymmdd(const QDateTime& t)
+long
+XcsvFormat::time_to_yyyymmdd(const QDateTime& t)
{
QDate d = t.date();
return d.year() * 10000 + d.month() * 100 + d.day();
}
-static garmin_fs_t*
-gmsd_init(Waypoint* wpt)
+garmin_fs_t*
+XcsvFormat::gmsd_init(Waypoint* wpt)
{
garmin_fs_t* gmsd = garmin_fs_t::find(wpt);
if (gmsd == nullptr) {
/* xcsv_parse_val() - parse incoming data into the waypt structure. */
/* usage: xcsv_parse_val("-123.34", *waypt, *field_map) */
/*****************************************************************************/
-static void
-xcsv_parse_val(const QString& value, Waypoint* wpt, const field_map& fmp,
+void
+XcsvFormat::xcsv_parse_val(const QString& value, Waypoint* wpt, const XcsvStyle::field_map& fmp,
xcsv_parse_data* parse_data, const int line_no)
{
const char* enclosure = "";
}
/*****************************************************************************/
-/* xcsv_data_read() - read input file, parsing lines, fields and handling */
+/* read() - read input file, parsing lines, fields and handling */
/* any data conversion (the input meat) */
/*****************************************************************************/
-static void
-xcsv_data_read()
+void
+XcsvFormat::read()
{
int linecount = 0;
route_head* rte = nullptr;
/* now rip the line apart */
for (const auto& value : values) {
- const field_map& fmp = xcsv_style->ifields.at(ifield_idx++);
+ const XcsvStyle::field_map& fmp = xcsv_style->ifields.at(ifield_idx++);
xcsv_parse_val(value, wpt_tmp, fmp, &parse_data, linecount);
if (ifield_idx >= xcsv_style->ifields.size()) {
}
}
-static void
-xcsv_resetpathlen(const route_head* head)
+void
+XcsvFormat::xcsv_resetpathlen(const route_head* head)
{
pathdist = 0;
oldlat = 999;
/* xcsv_waypt_pr() - write output file, handling output conversions */
/* (the output meat) */
/*****************************************************************************/
-static void
-xcsv_waypt_pr(const Waypoint* wpt)
+void
+XcsvFormat::xcsv_waypt_pr(const Waypoint* wpt)
{
QString buff;
double latitude, longitude;
*/
int field_is_unknown = 0;
- if ((i != 0) && !(fmp.options & options_nodelim)) {
+ if ((i != 0) && !(fmp.options & XcsvStyle::options_nodelim)) {
xcsv_file->stream << write_delimiter;
}
- if (fmp.options & options_absolute) {
+ if (fmp.options & XcsvStyle::options_absolute) {
lat = fabs(lat);
lon = fabs(lon);
}
buff = QString::asprintf(fmp.printfc.constData(), waypt_out_count + atoi(fmp.val.constData()));
break;
case XT_CONSTANT: {
- auto cp = xcsv_get_char_from_constant_table(fmp.val.constData());
+ auto cp = XcsvStyle::xcsv_get_char_from_constant_table(fmp.val.constData());
if (!cp.isEmpty()) {
buff = QString::asprintf(fmp.printfc.constData(), CSTR(cp));
} else {
}
QString obuff = csv_stringclean(buff, xcsv_style->badchars);
- if (field_is_unknown && fmp.options & options_optional) {
+ if (field_is_unknown && fmp.options & XcsvStyle::options_optional) {
continue;
}
}
// return |original| after performing token replacement.
-static QString
-xcsv_replace_tokens(const QString& original) {
+QString
+XcsvFormat::xcsv_replace_tokens(const QString& original) const {
QString replacement = original;
// Don't do potentially expensive replacements if token prefix
// isn't present;
}
/*****************************************************************************/
-/* xcsv_data_write(void) - write prologues, spawn the output loop, and write */
+/* write(void) - write prologues, spawn the output loop, and write */
/* epilogues. */
/*****************************************************************************/
-static void
-xcsv_data_write()
+void
+XcsvFormat::write()
{
/* reset the index counter */
waypt_out_count = 0;
xcsv_file->stream << line_to_write << xcsv_style->record_delimiter;
}
+ auto xcsv_waypt_pr_lambda = [this](const Waypoint* wpt)->void {
+ xcsv_waypt_pr(wpt);
+ };
+ auto xcsv_resetpathlen_lambda = [this](const route_head* rte)->void {
+ xcsv_resetpathlen(rte);
+ };
+
if ((xcsv_style->datatype == 0) || (xcsv_style->datatype == wptdata)) {
- waypt_disp_all(xcsv_waypt_pr);
+ waypt_disp_all(xcsv_waypt_pr_lambda);
}
if ((xcsv_style->datatype == 0) || (xcsv_style->datatype == rtedata)) {
- route_disp_all(xcsv_resetpathlen, nullptr, xcsv_waypt_pr);
+ route_disp_all(xcsv_resetpathlen_lambda, nullptr, xcsv_waypt_pr_lambda);
}
if ((xcsv_style->datatype == 0) || (xcsv_style->datatype == trkdata)) {
- track_disp_all(xcsv_resetpathlen, nullptr, xcsv_waypt_pr);
+ track_disp_all(xcsv_resetpathlen_lambda, nullptr, xcsv_waypt_pr_lambda);
}
/* output epilogue lines, if any. */
}
}
-static void
-xcsv_parse_style_line(XcsvStyle* style, QString line)
+void
+XcsvStyle::xcsv_parse_style_line(XcsvStyle* style, QString line)
{
// The lines to be parsed have a leading operation |op| that is
// separated by whitespace from the rest. Each op may have zero or
* a terminating null. Makes multiple calls to that function so
* that "ignore to end of line" comments work right.
*/
-static XcsvStyle
-xcsv_parse_style_buff(const char* sbuff)
+XcsvStyle
+XcsvStyle::xcsv_parse_style_buff(const char* sbuff)
{
XcsvStyle style;
const QStringList lines = QString(sbuff).split('\n');
return style;
}
-static XcsvStyle
-xcsv_read_style(const char* fname)
+XcsvStyle
+XcsvStyle::xcsv_read_style(const char* fname)
{
gbfile* fp = gbfopen(fname, "rb", MYNAME);
XcsvStyle style;
* the xcsv parser and make it ready for general use.
*/
XcsvStyle
-xcsv_read_internal_style(const char* style_buf)
+XcsvStyle::xcsv_read_internal_style(const char* style_buf)
{
XcsvStyle style = xcsv_parse_style_buff(style_buf);
}
void
-xcsv_setup_internal_style(const char* style_buf)
+XcsvFormat::xcsv_setup_internal_style(const char* style_buf)
{
intstylebuf = style_buf;
}
-static void
-xcsv_rd_init(const QString& fname)
+void
+XcsvFormat::rd_init(const QString& fname)
{
/*
* if we don't have an internal style defined, we need to
* read it from a user-supplied style file, or die trying.
*/
if (intstylebuf != nullptr) {
- xcsv_style = new XcsvStyle(xcsv_read_internal_style(intstylebuf));
+ xcsv_style = new XcsvStyle(XcsvStyle::xcsv_read_internal_style(intstylebuf));
} else {
if (!styleopt) {
fatal(MYNAME ": XCSV input style not declared. Use ... -i xcsv,style=path/to/file.style\n");
}
- xcsv_style = new XcsvStyle(xcsv_read_style(styleopt));
+ xcsv_style = new XcsvStyle(XcsvStyle::xcsv_read_style(styleopt));
}
if ((xcsv_style->datatype == 0) || (xcsv_style->datatype == wptdata)) {
assert(gps_datum_wgs84 == GPS_Lookup_Datum_Index("WGS 84"));
}
-static void
-xcsv_rd_deinit()
+void
+XcsvFormat::rd_deinit()
{
xcsv_file->stream.close();
delete xcsv_file;
xcsv_style = nullptr;
}
-static void
-xcsv_wr_init(const QString& fname)
+void
+XcsvFormat::wr_init(const QString& fname)
{
/*
* if we don't have an internal style defined, we need to
* read it from a user-supplied style file, or die trying.
*/
if (intstylebuf != nullptr) {
- xcsv_style = new XcsvStyle(xcsv_read_internal_style(intstylebuf));
+ xcsv_style = new XcsvStyle(XcsvStyle::xcsv_read_internal_style(intstylebuf));
} else {
if (!styleopt) {
fatal(MYNAME ": XCSV output style not declared. Use ... -o xcsv,style=path/to/file.style\n");
}
- xcsv_style = new XcsvStyle(xcsv_read_style(styleopt));
+ xcsv_style = new XcsvStyle(XcsvStyle::xcsv_read_style(styleopt));
}
xcsv_file = new XcsvFile;
assert(gps_datum_wgs84 == GPS_Lookup_Datum_Index("WGS 84"));
}
-static void
-xcsv_wr_position_init(const QString& fname)
+void
+XcsvFormat::wr_position_init(const QString& fname)
{
- xcsv_wr_init(fname);
+ wr_init(fname);
}
-static void
-xcsv_wr_deinit()
+void
+XcsvFormat::wr_deinit()
{
xcsv_file->stream.close();
delete xcsv_file;
xcsv_style = nullptr;
}
-static void
-xcsv_wr_position_deinit()
+void
+XcsvFormat::wr_position_deinit()
{
- xcsv_wr_deinit();
+ wr_deinit();
}
-static void
-xcsv_wr_position(Waypoint* wpt)
+void
+XcsvFormat::wr_position(Waypoint* wpt)
{
/* Tweak incoming name if we don't have a fix */
switch (wpt->fix) {
}
waypt_add(wpt);
- xcsv_data_write();
+ write();
waypt_del(wpt);
xcsv_file->stream.flush();
}
-
-ff_vecs_t xcsv_vecs = {
- ff_type_internal,
- FF_CAP_RW_WPT, /* This is a bit of a lie for now... */
- xcsv_rd_init,
- xcsv_wr_init,
- xcsv_rd_deinit,
- xcsv_wr_deinit,
- xcsv_data_read,
- xcsv_data_write,
- nullptr,
- &xcsv_args,
- CET_CHARSET_UTF8, 0, /* conversion to utf8 for gmsd is handled by the stream, don't let csv_convert_strings convert gmsd data */
- { nullptr, nullptr, nullptr, xcsv_wr_position_init, xcsv_wr_position, xcsv_wr_position_deinit },
- nullptr
-
-};
#endif //CSVFMTS_ENABLED
#ifndef XCSV_H_INCLUDED_
#define XCSV_H_INCLUDED_
+#include <ctime>
#include <utility> // for move
#include <QtCore/QByteArray> // for QByteArray
+#include <QtCore/QDateTime> // for QDateTime
#include <QtCore/QList> // for QList
#include <QtCore/QString> // for QString
#include <QtCore/QStringList> // for QStringList
+#include <QtCore/QVector> // for QVector
#include "defs.h"
+#include "format.h"
+#include "garmin_fs.h"
+#include "src/core/datetime.h" // for DateTime
#include "src/core/optional.h" // for optional
#include "src/core/textstream.h" // for TextStream
#if CSVFMTS_ENABLED
-/****************************************************************************/
-/* types required for various xcsv functions */
-/****************************************************************************/
+/*
+ * Class describing an xcsv format.
+ */
-class XcsvFile {
+class XcsvStyle
+{
public:
- XcsvFile() : mkshort_handle(mkshort_new_handle()) {}
- // delete copy and move constructors and assignment operators.
- // The defaults are not appropriate, and we haven't implemented proper ones.
- XcsvFile(const XcsvFile&) = delete;
- XcsvFile& operator=(const XcsvFile&) = delete;
- XcsvFile(XcsvFile&&) = delete;
- XcsvFile& operator=(XcsvFile&&) = delete;
- ~XcsvFile() {
- if (mkshort_handle != nullptr) {
- mkshort_del_handle(&mkshort_handle);
- }
- }
+ /* Types */
- gpsbabel::TextStream stream;
- QString fname;
- int gps_datum_idx{-1}; /* result of GPS_Lookup_Datum_Index */
- short_handle mkshort_handle{nullptr};
-};
+ /* something to map fields to waypts */
+ struct field_map {
+ // We use QByteArrays because consumers want char* data and QByteArrays supply this through constData().
+ // If we used QStrings, then we would have to convert to QByteArrays to get the char* data.
+ // If we use char* then we have to manage memory allocation/deallocation.
+ // TODO: when consumers use QStrings then we can store QStrings instead of QByteArrays.
+ QByteArray key;
+ QByteArray val;
+ QByteArray printfc;
+ int hashed_key{0};
+ unsigned options{0};
-/* something to map fields to waypts */
-constexpr unsigned options_nodelim = 1;
-constexpr unsigned options_absolute = 2;
-constexpr unsigned options_optional = 4;
+ field_map() = default;
+ field_map(QByteArray k, QByteArray v, QByteArray p, int hk) : key{std::move(k)},val{std::move(v)},printfc{std::move(p)},hashed_key{hk} {}
+ field_map(QByteArray k, QByteArray v, QByteArray p, int hk, unsigned o) : key{std::move(k)},val{std::move(v)},printfc{
+ std::move(p)},hashed_key{hk},options{o} {}
+ };
-struct field_map {
-public:
- // We use QByteArrays because consumers want char* data and QByteArrays supply this through constData().
- // If we used QStrings, then we would have to convert to QByteArrays to get the char* data.
- // If we use char* then we have to manage memory allocation/deallocation.
- // TODO: when consumers use QStrings then we can store QStrings instead of QByteArrays.
- QByteArray key;
- QByteArray val;
- QByteArray printfc;
- int hashed_key{0};
- unsigned options{0};
-
- field_map() = default;
- field_map(QByteArray k, QByteArray v, QByteArray p, int hk) : key{std::move(k)},val{std::move(v)},printfc{std::move(p)},hashed_key{hk} {}
- field_map(QByteArray k, QByteArray v, QByteArray p, int hk, unsigned o) : key{std::move(k)},val{std::move(v)},printfc{
- std::move(p)},hashed_key{hk},options{o} {}
-};
+ /* Constants */
+
+ static constexpr unsigned options_nodelim = 1;
+ static constexpr unsigned options_absolute = 2;
+ static constexpr unsigned options_optional = 4;
+
+ /* Member Functions */
+
+ static QString xcsv_get_char_from_constant_table(const QString& key);
+ static XcsvStyle xcsv_read_internal_style(const char* style_buf);
+ static XcsvStyle xcsv_read_style(const char* fname);
+
+ /* Data Members */
-/*
- * Class describing an xcsv format.
- */
-struct XcsvStyle {
/* PROLOGUE from style file */
/* header lines for writing at the top of the file. */
QStringList prologue;
/* SHORTWHITE from style file */
gpsbabel_optional::optional<int> whitespace_ok;
+
+private:
+ /* Types */
+
+ /* something to map config file constants to chars */
+ struct char_map_t {
+ const QString key;
+ const QString chars;
+ };
+
+ /* Member Functions */
+
+ static QString dequote(const QString& in);
+ static void validate_fieldmap(const field_map& fmp, bool is_output);
+ static void xcsv_ifield_add(XcsvStyle* style, const QString& qkey, const QString& qval, const QString& qpfc);
+ static void xcsv_ofield_add(XcsvStyle* style, const QString& qkey, const QString& qval, const QString& qpfc, unsigned int options);
+ static void xcsv_parse_style_line(XcsvStyle* style, QString line);
+ static XcsvStyle xcsv_parse_style_buff(const char* sbuff);
+
+ /* Data Members */
+
+ /* a table of config file constants mapped to chars */
+ static const char_map_t xcsv_char_table[];
};
-/* public function prototypes */
+class XcsvFormat : public Format
+{
+public:
+ /* Member Functions */
+ QVector<arglist_t>* get_args() override
+ {
+ return &xcsv_args;
+ }
+
+ ff_type get_type() const override
+ {
+ return ff_type_internal;
+ }
+
+ QVector<ff_cap> get_cap() const override
+ {
+ return FF_CAP_RW_WPT; /* This is a bit of a lie for now... */
+ }
+
+ QString get_encode() const override
+ {
+ return CET_CHARSET_UTF8;
+ }
+
+ int get_fixed_encode() const override
+ {
+ return 0;
+ }
-void xcsv_setup_internal_style(const char* style_buf);
-XcsvStyle xcsv_read_internal_style(const char* style_buf);
+ void rd_init(const QString& fname) override;
+ void read() override;
+ void rd_deinit() override;
+ void wr_init(const QString& fname) override;
+ void write() override;
+ void wr_deinit() override;
+ void wr_position_init(const QString& fname) override;
+ void wr_position(Waypoint* wpt) override;
+ void wr_position_deinit() override;
+
+ void xcsv_setup_internal_style(const char* style_buf);
+
+private:
+ /* Types */
+
+ class XcsvFile
+ {
+ public:
+ /* Special Member Functions */
+
+ XcsvFile() : mkshort_handle(mkshort_new_handle()) {}
+ // delete copy and move constructors and assignment operators.
+ // The defaults are not appropriate, and we haven't implemented proper ones.
+ XcsvFile(const XcsvFile&) = delete;
+ XcsvFile& operator=(const XcsvFile&) = delete;
+ XcsvFile(XcsvFile&&) = delete;
+ XcsvFile& operator=(XcsvFile&&) = delete;
+ ~XcsvFile()
+ {
+ if (mkshort_handle != nullptr) {
+ mkshort_del_handle(&mkshort_handle);
+ }
+ }
+
+ /* Data Members */
+
+ gpsbabel::TextStream stream;
+ QString fname;
+ int gps_datum_idx{-1}; /* result of GPS_Lookup_Datum_Index */
+ short_handle mkshort_handle{nullptr};
+ };
+
+ struct xcsv_parse_data {
+ QString rte_name;
+ QString trk_name;
+ bool new_track{false};
+ double utm_northing{0};
+ double utm_easting{0};
+ double utm_zone{0};
+ char utm_zonec{'N'};
+ UrlLink* link_{nullptr};
+ gpsbabel_optional::optional<bool> lat_dir_positive;
+ gpsbabel_optional::optional<bool> lon_dir_positive;
+ };
+
+ /* Constants */
+
+ static constexpr char lat_dir(double a)
+ {
+ return a < 0.0 ? 'S' : 'N';
+ }
+ static constexpr char lon_dir(double a)
+ {
+ return a < 0.0 ? 'W' : 'E';
+ }
+
+ /* convert excel time (days since 1900) to time_t and back again */
+ static constexpr double excel_to_timet(double a)
+ {
+ return (a - 25569.0) * 86400.0;
+ }
+ static constexpr double timet_to_excel(double a)
+ {
+ return (a / 86400.0) + 25569.0;
+ }
+
+ static constexpr int gps_datum_wgs84 = 118; // GPS_Lookup_Datum_Index("WGS 84")
+
+ /* Member Functions */
+
+ static QDateTime yyyymmdd_to_time(const char* s);
+ static time_t sscanftime(const char* s, const char* format, int gmt);
+ static time_t addhms(const char* s, const char* format);
+ static QString writetime(const char* format, time_t t, bool gmt);
+ static QString writetime(const char* format, const gpsbabel::DateTime& t, bool gmt);
+ static QString writehms(const char* format, time_t t, int gmt);
+ static QString writehms(const char* format, const gpsbabel::DateTime& t, int gmt);
+ static long int time_to_yyyymmdd(const QDateTime& t);
+ static garmin_fs_t* gmsd_init(Waypoint* wpt);
+ static void xcsv_parse_val(const QString& value, Waypoint* wpt, const XcsvStyle::field_map& fmp, xcsv_parse_data* parse_data, int line_no);
+ void xcsv_resetpathlen(const route_head* head);
+ void xcsv_waypt_pr(const Waypoint* wpt);
+ QString xcsv_replace_tokens(const QString& original) const;
+
+ /* Data Members */
+
+ XcsvFile* xcsv_file{nullptr};
+ const XcsvStyle* xcsv_style{nullptr};
+ double pathdist = 0;
+ double oldlon = 999;
+ double oldlat = 999;
+
+ int waypt_out_count = 0;
+ const route_head* csv_track = nullptr;
+ const route_head* csv_route = nullptr;
+
+ char* styleopt = nullptr;
+ char* snlenopt = nullptr;
+ char* snwhiteopt = nullptr;
+ char* snupperopt = nullptr;
+ char* snuniqueopt = nullptr;
+ char* prefer_shortnames = nullptr;
+ char* xcsv_urlbase = nullptr;
+ char* opt_datum = nullptr;
+
+ const char* intstylebuf = nullptr;
+
+ QVector<arglist_t> xcsv_args = {
+ {
+ "style", &styleopt, "Full path to XCSV style file", nullptr,
+ ARGTYPE_FILE | ARGTYPE_REQUIRED, ARG_NOMINMAX, nullptr
+ },
+ {
+ "snlen", &snlenopt, "Max synthesized shortname length", nullptr,
+ ARGTYPE_INT, "1", nullptr, nullptr
+ },
+ {
+ "snwhite", &snwhiteopt, "Allow whitespace synth. shortnames",
+ nullptr, ARGTYPE_BOOL, ARG_NOMINMAX, nullptr
+ },
+ {
+ "snupper", &snupperopt, "UPPERCASE synth. shortnames",
+ nullptr, ARGTYPE_BOOL, ARG_NOMINMAX, nullptr
+ },
+ {
+ "snunique", &snuniqueopt, "Make synth. shortnames unique",
+ nullptr, ARGTYPE_BOOL, ARG_NOMINMAX, nullptr
+ },
+ {
+ "urlbase", &xcsv_urlbase, "Basename prepended to URL on output",
+ nullptr, ARGTYPE_STRING, ARG_NOMINMAX, nullptr
+ },
+ {
+ "prefer_shortnames", &prefer_shortnames,
+ "Use shortname instead of description",
+ nullptr, ARGTYPE_BOOL, ARG_NOMINMAX, nullptr
+ },
+ {
+ "datum", &opt_datum, "GPS datum (def. WGS 84)",
+ nullptr, ARGTYPE_STRING, ARG_NOMINMAX, nullptr
+ },
+ };
+
+};
#endif // CSVFMTS_ENABLED
#endif // XCSV_H_INCLUDED_